package com.ftwj.demo.base.common;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;

import com.ftwj.demo.base.cache.CustomCache;
import com.ftwj.demo.base.constant.Const;
import com.ftwj.demo.base.util.CacheUtil;
import com.ftwj.demo.base.util.PageInfoBean;

import cn.hutool.core.util.StrUtil;

/**
* <p>Title: BaseJdbcTemplate</p>  
* <p>Description: 描述: 由于默认JdbcTemplate没有缓存实现，此方法用于装饰原JdbcTemplate方法，实现缓存</p>
* <p>Copyright: Copyright (c) 2020 </p>  
* <p>Company: 兰州飞天网景信息产业有限公司</p>    
* @author WangSLi
* @date 2020年4月23日 下午8:54:08
* @version V1.0
 */
@Component
public class BaseJdbcTemplate {
	
	@Autowired
	private JdbcTemplate jtl;
	@Value("${spring.jdbctemplate.cache:false}")
	private boolean open;
	private String key;
	private CustomCache cache;
	
	@SuppressWarnings("unchecked")
	public <T> T query(String sql, ResultSetExtractor<T> rse) throws DataAccessException {
		if(!open) return jtl.query(sql, rse);
		cache=CacheUtil.getCache();
		key=CacheUtil.getKey(sql,rse);
		T res = (T) cache.get(key);
		if(res!=null) return res;
		T data = jtl.query(sql, rse);
		cache.put(key, data);
		return data;
	}
	
	@SuppressWarnings("unchecked")
	public <T> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException {
		if(!open) return jtl.query(sql, rowMapper);
		cache=CacheUtil.getCache();
		key=CacheUtil.getKey(sql,rowMapper);
		List<T> res = (List<T>) cache.get(key);
		if(res!=null) return res;
		List<T> data = jtl.query(sql, rowMapper);
		cache.put(key, data);
		return data;
	}
	
	@SuppressWarnings("unchecked")
	public <T> T queryForObject(String sql, RowMapper<T> rowMapper) throws DataAccessException {
		if(!open) return jtl.queryForObject(sql, rowMapper);
		cache=CacheUtil.getCache();
		key=CacheUtil.getKey(sql,rowMapper);
		T res = (T) cache.get(key);
		if(res!=null) return res;
		T data = jtl.queryForObject(sql, rowMapper);
		cache.put(key, data);
		return data;
	}
	
	@SuppressWarnings("unchecked")
	public <T> T queryForObject(String sql, Class<T> requiredType) throws DataAccessException {
		if(!open) return jtl.queryForObject(sql, requiredType);
		cache=CacheUtil.getCache();
		key=CacheUtil.getKey(sql,requiredType);
		T res = (T) cache.get(key);
		if(res!=null) return res;
		T data = jtl.queryForObject(sql, requiredType);
		cache.put(key, data);
		return data;
	}
	
	@SuppressWarnings("unchecked")
	public Map<String, Object> queryForMap(String sql) throws DataAccessException {
		if(!open) return jtl.queryForMap(sql);
		cache=CacheUtil.getCache();
		key=CacheUtil.getKey(sql);
		Map<String, Object> res = (Map<String, Object>) cache.get(key);
		if(res!=null) return res;
		Map<String, Object> data = jtl.queryForMap(sql);
		cache.put(key, data);
		return data;
	}
	
	@SuppressWarnings("unchecked")
	public <T> List<T> queryForList(String sql, Class<T> elementType) throws DataAccessException {
		if(!open) return jtl.queryForList(sql, elementType);
		cache=CacheUtil.getCache();
		key=CacheUtil.getKey(sql,elementType);
		List<T> res = (List<T>) cache.get(key);
		if(res!=null) return res;
		List<T> data = jtl.queryForList(sql, elementType);
		cache.put(key, data);
		return data;
	}
	
	@SuppressWarnings("unchecked")
	public List<Map<String, Object>> queryForList(String sql) throws DataAccessException {
		if(!open) return jtl.queryForList(sql);
		cache=CacheUtil.getCache();
		key=CacheUtil.getKey(sql);
		List<Map<String, Object>> res =  (List<Map<String, Object>>) cache.get(key);
		if(res!=null) return res;
		List<Map<String, Object>> data = jtl.queryForList(sql);
		cache.put(key, data);
		return data;
	}
	
	@SuppressWarnings("unchecked")
	public <T> T query(String sql, Object[] args, ResultSetExtractor<T> rse) throws DataAccessException {
		if(!open) return jtl.query(sql, args,rse);
		cache=CacheUtil.getCache();
		key=CacheUtil.getKey(sql,rse,args);
		T res = (T) cache.get(key);
		if(res!=null) return res;
		T data = jtl.query(sql, args,rse);
		cache.put(key, data);
		return data;
	}
	
	@SuppressWarnings("unchecked")
	public <T> T query(String sql, ResultSetExtractor<T> rse, Object... args) throws DataAccessException {
		if(!open) return jtl.query(sql,rse,args);
		cache=CacheUtil.getCache();
		key=CacheUtil.getKey(sql,rse,args);
		T res = (T) cache.get(key);
		if(res!=null) return res;
		T data = jtl.query(sql,rse,args);
		cache.put(key, data);
		return data;
	}
	
	@SuppressWarnings("unchecked")
	public <T> List<T> query(String sql, Object[] args, RowMapper<T> rowMapper) throws DataAccessException {
		if(!open) return jtl.query(sql, args, rowMapper);
		cache=CacheUtil.getCache();
		key=CacheUtil.getKey(sql,rowMapper,args);
		List<T> res = (List<T>) cache.get(key);
		if(res!=null) return res;
		List<T> data = jtl.query(sql, args, rowMapper);
		cache.put(key, data);
		return data;
	}

	@SuppressWarnings("unchecked")
	public <T> List<T> query(String sql, RowMapper<T> rowMapper, Object... args) throws DataAccessException {
		if(!open) return jtl.query(sql,rowMapper,args);
		cache=CacheUtil.getCache();
		key=CacheUtil.getKey(sql,rowMapper,args);
		List<T> res = (List<T>) cache.get(key);
		if(res!=null) return res;
		List<T> data = jtl.query(sql,rowMapper,args);
		cache.put(key, data);
		return data;
	}
	
	@SuppressWarnings("unchecked")
	public <T> List<T> query(String sql, PreparedStatementSetter pss, RowMapper<T> rowMapper) throws DataAccessException {
		if(!open) return jtl.query(sql,pss,rowMapper);
		cache=CacheUtil.getCache();
		key=CacheUtil.getKey(sql,rowMapper,pss);
		List<T> res = (List<T>) cache.get(key);
		if(res!=null) return res;
		List<T> data = jtl.query(sql,pss,rowMapper);
		cache.put(key, data);
		return data;
	}
	
	@SuppressWarnings("unchecked")
	public <T> T queryForObject(String sql, Object[] args, RowMapper<T> rowMapper) throws DataAccessException {
		if(!open) return jtl.queryForObject(sql,args,rowMapper);
		cache=CacheUtil.getCache();
		key=CacheUtil.getKey(sql,rowMapper,args);
		T res = (T) cache.get(key);
		if(res!=null) return res;
		T data = jtl.queryForObject(sql,args,rowMapper);
		cache.put(key, data);
		return data;
	}
	
	@SuppressWarnings("unchecked")
	public <T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args) throws DataAccessException {
		if(!open) return jtl.queryForObject(sql,rowMapper,args);
		cache=CacheUtil.getCache();
		key=CacheUtil.getKey(sql,rowMapper,args);
		T res = (T) cache.get(key);
		if(res!=null) return res;
		T data = jtl.queryForObject(sql,rowMapper,args);
		cache.put(key, data);
		return data;
	}
	
	@SuppressWarnings("unchecked")
	public <T> T queryForObject(String sql, Object[] args, Class<T> requiredType) throws DataAccessException {
		if(!open) return jtl.queryForObject(sql,args,requiredType);
		cache=CacheUtil.getCache();
		key=CacheUtil.getKey(sql,requiredType,args);
		T res = (T) cache.get(key);
		if(res!=null) return res;
		T data = jtl.queryForObject(sql,args,requiredType);
		cache.put(key, data);
		return data;
	}
	
	@SuppressWarnings("unchecked")
	public <T> T queryForObject(String sql, Class<T> requiredType, @Nullable Object... args) throws DataAccessException {
		if(!open) return jtl.queryForObject(sql,requiredType,args);
		cache=CacheUtil.getCache();
		key=CacheUtil.getKey(sql,requiredType,args);
		T res = (T) cache.get(key);
		if(res!=null) return res;
		T data = jtl.queryForObject(sql,requiredType,args);
		cache.put(key, data);
		return data;
	}
	
	@SuppressWarnings("unchecked")
	public Map<String, Object> queryForMap(String sql, Object... args) throws DataAccessException {
		if(!open) return jtl.queryForMap(sql,args);
		cache=CacheUtil.getCache();
		key=CacheUtil.getKey(sql,args);
		Map<String, Object> res = (Map<String, Object>) cache.get(key);
		if(res!=null) return res;
		Map<String, Object> data = jtl.queryForMap(sql,args);
		cache.put(key, data);
		return data;
	}
	
	@SuppressWarnings("unchecked")
	public <T> List<T> queryForList(String sql, Object[] args, Class<T> elementType) throws DataAccessException {
		if(!open) return jtl.queryForList(sql,args,elementType);
		cache=CacheUtil.getCache();
		key=CacheUtil.getKey(sql,elementType,args);
		List<T> res = (List<T>) cache.get(key);
		if(res!=null) return res;
		List<T> data = jtl.queryForList(sql,args,elementType);
		cache.put(key, data);
		return data;
	}
	
	@SuppressWarnings("unchecked")
	public <T> List<T> queryForList(String sql, Class<T> elementType, Object... args) throws DataAccessException {
		if(!open) return jtl.queryForList(sql,elementType,args);
		cache=CacheUtil.getCache();
		key=CacheUtil.getKey(sql,elementType,args);
		List<T> res = (List<T>) cache.get(key);
		if(res!=null) return res;
		List<T> data = jtl.queryForList(sql,elementType,args);
		cache.put(key, data);
		return data;
	}
	
	@SuppressWarnings("unchecked")
	public List<Map<String, Object>> queryForList(String sql, Object... args) throws DataAccessException {
		if(!open) return jtl.queryForList(sql,args);
		cache=CacheUtil.getCache();
		key=CacheUtil.getKey(sql,args);
		List<Map<String, Object>> res = (List<Map<String, Object>>) cache.get(key);
		if(res!=null) return res;
		List<Map<String, Object>> data = jtl.queryForList(sql,args);
		cache.put(key, data);
		return data;
	}
	
	public int update(String sql) throws DataAccessException {
		if(!open) return jtl.update(sql);
		cache=CacheUtil.getCache();
		cache.clear();
		return jtl.update(sql);
	}
	
	public int update(PreparedStatementCreator psc) throws DataAccessException {
		if(!open) return jtl.update(psc);
		cache=CacheUtil.getCache();
		cache.clear();
		return jtl.update(psc);
	}

	public int update(PreparedStatementCreator psc, KeyHolder generatedKeyHolder) throws DataAccessException {
		if(!open) return jtl.update(psc,generatedKeyHolder);
		cache=CacheUtil.getCache();
		cache.clear();
		return jtl.update(psc,generatedKeyHolder);
	}
	
	public int update(String sql, Object... args) throws DataAccessException {
		if(!open) return jtl.update(sql, args);
		cache=CacheUtil.getCache();
		cache.clear();
		return jtl.update(sql, args);
	}
	
	public int[] batchUpdate(String... sql) throws DataAccessException {
		if(!open) return jtl.batchUpdate(sql);
		cache=CacheUtil.getCache();
		cache.clear();
		return jtl.batchUpdate(sql);
	}
	
	public int[] batchUpdate(String sql, List<Object[]> batchArgs) throws DataAccessException {
		if(!open) return jtl.batchUpdate(sql, batchArgs);
		cache=CacheUtil.getCache();
		cache.clear();
		return jtl.batchUpdate(sql, batchArgs);
	}
	
	public PageInfoBean page(String sql,PageInfoBean pageInfoBean) {
		String totalSql = "SELECT COUNT(1) FROM ({}) AS total";
		totalSql=StrUtil.format(totalSql, sql);
		int total = jtl.queryForObject(totalSql, Integer.class);
		pageInfoBean.setTotalNum(total);
		pageInfoBean.calPageParam();
		String pageSql = sql+Const.MYSQL_PAGE_SQL;
		List<Map<String, Object>> list = jtl.queryForList(pageSql, pageInfoBean.getStartIndex(),pageInfoBean.getPerPage());
		pageInfoBean.setRecList(list);
		return pageInfoBean;
	}
	
	public <T> PageInfoBean page(String sql, Class<T> requiredType, PageInfoBean pageInfoBean) {
		String totalSql = "SELECT COUNT(1) FROM ({}) AS total";
		totalSql=StrUtil.format(totalSql, sql);
		int total = jtl.queryForObject(totalSql, Integer.class);
		pageInfoBean.setTotalNum(total);
		pageInfoBean.calPageParam();
		String pageSql = sql+Const.MYSQL_PAGE_SQL;
		List<T> list = jtl.query(pageSql, new Object[] { pageInfoBean.getStartIndex(),pageInfoBean.getPerPage()}, new BeanPropertyRowMapper<T>(requiredType));
		pageInfoBean.setRecList(list);
		return pageInfoBean;
	}
	
	public PageInfoBean page(String sql, PageInfoBean pageInfoBean, Object... args) {
		String totalSql = "SELECT COUNT(1) FROM ({}) AS total";
		totalSql=StrUtil.format(totalSql, sql);
		int total = jtl.queryForObject(totalSql, Integer.class,args);
		pageInfoBean.setTotalNum(total);
		pageInfoBean.calPageParam();
		String pageSql = sql+Const.MYSQL_PAGE_SQL;
		
		Object[] arr2 = arrayDilatation(args,pageInfoBean.getStartIndex(),pageInfoBean.getPerPage());
		List<Map<String, Object>> list = jtl.queryForList(pageSql, arr2);
		pageInfoBean.setRecList(list);
		return pageInfoBean;
	}
	
	public <T> PageInfoBean page(String sql, Class<T> requiredType, PageInfoBean pageInfoBean, Object... args) {
		String totalSql = "SELECT COUNT(1) FROM ({}) AS total";
		totalSql=StrUtil.format(totalSql, sql);
		int total = jtl.queryForObject(totalSql, Integer.class,args);
		pageInfoBean.setTotalNum(total);
		pageInfoBean.calPageParam();
		String pageSql = sql+Const.MYSQL_PAGE_SQL;
		Object[] arr2 = arrayDilatation(args,pageInfoBean.getStartIndex(),pageInfoBean.getPerPage());
		List<T> list = jtl.queryForList(pageSql, arr2, requiredType);
		pageInfoBean.setRecList(list);
		return pageInfoBean;
	}
	
	public <T> PageInfoBean page(String sql, Object[] args, Class<T> requiredType, PageInfoBean pageInfoBean) {
		String totalSql = "SELECT COUNT(1) FROM ({}) AS total";
		totalSql=StrUtil.format(totalSql, sql);
		int total = jtl.queryForObject(totalSql, Integer.class,args);
		pageInfoBean.setTotalNum(total);
		pageInfoBean.calPageParam();
		String pageSql = sql+Const.MYSQL_PAGE_SQL;
		Object[] arr2 = arrayDilatation(args,pageInfoBean.getStartIndex(),pageInfoBean.getPerPage());
		List<T> list = jtl.queryForList(pageSql, arr2, requiredType);
		pageInfoBean.setRecList(list);
		return pageInfoBean;
	}
	
	public <T> PageInfoBean page(String sql,  RowMapper<T> rowMapper, PageInfoBean pageInfoBean) {
		String totalSql = "SELECT COUNT(1) FROM ({}) AS total";
		totalSql=StrUtil.format(totalSql, sql);
		int total = jtl.queryForObject(totalSql, Integer.class);
		pageInfoBean.setTotalNum(total);
		pageInfoBean.calPageParam();
		String pageSql = sql+Const.MYSQL_PAGE_SQL;
		List<Map<String, Object>> list = jtl.queryForList(pageSql, pageInfoBean.getStartIndex(),pageInfoBean.getPerPage(), rowMapper);
		pageInfoBean.setRecList(list);
		return pageInfoBean;
	}
	
	public <T> PageInfoBean page(String sql,  RowMapper<T> rowMapper, PageInfoBean pageInfoBean, Object... args) {
		String totalSql = "SELECT COUNT(1) FROM ({}) AS total";
		totalSql=StrUtil.format(totalSql, sql);
		int total = jtl.queryForObject(totalSql, Integer.class,args);
		pageInfoBean.setTotalNum(total);
		pageInfoBean.calPageParam();
		String pageSql = sql+Const.MYSQL_PAGE_SQL;
		Object[] arr2 = arrayDilatation(args,pageInfoBean.getStartIndex(),pageInfoBean.getPerPage());
		List<Map<String, Object>> list = jtl.queryForList(pageSql, arr2, rowMapper);
		pageInfoBean.setRecList(list);
		return pageInfoBean;
	}
	
	public <T> PageInfoBean page(String sql, Object[] args,  RowMapper<T> rowMapper, PageInfoBean pageInfoBean) {
		String totalSql = "SELECT COUNT(1) FROM ({}) AS total";
		totalSql=StrUtil.format(totalSql, sql);
		int total = jtl.queryForObject(totalSql, Integer.class,args);
		pageInfoBean.setTotalNum(total);
		pageInfoBean.calPageParam();
		String pageSql = sql+Const.MYSQL_PAGE_SQL;
		Object[] arr2 = arrayDilatation(args,pageInfoBean.getStartIndex(),pageInfoBean.getPerPage());
		List<Map<String, Object>> list = jtl.queryForList(pageSql, arr2, rowMapper);
		pageInfoBean.setRecList(list);
		return pageInfoBean;
	}
	
	/**
	 * <p>Title: arrayDilatation</p>  
	 * <p>Description: 数组扩容</p>  
	 * @param args
	 * @param startIndex
	 * @param perPage
	 * @author HeYang
	 * @date 2020年5月22日  
	 * @version 1.0
	 */
	private Object[] arrayDilatation(Object[] args, Integer startIndex, Integer perPage) {
		Object[] arr2 = Arrays.copyOf(args, (args.length + 2));
		arr2[arr2.length-2] = startIndex;
		arr2[arr2.length-1] = perPage;
		return arr2;
	}
	
}

